#include "stdafx.h"
#include "SelectionDialog.h"
#include "Resource.h"
#include <assert.h>
#include <string>


SelectionDialog::SelectionDialog(LONGLONG inFileLength)
{
	mFileLength = inFileLength;
}

bool SelectionDialog::DoModal(HINSTANCE inInstance, HWND inParentWindow)
{
	INT_PTR result = ::DialogBoxParam(inInstance, 
		MAKEINTRESOURCE(IDD_DIALOG_SELECTIONRANGE),
		inParentWindow, &SelectionDialog::DialogProc,
		reinterpret_cast<LPARAM>(this));

	if (-1 == result)
	{
		DWORD err = ::GetLastError();
		char buf[100];
		sprintf_s(buf, "err = %x (%d)", err, err);
		::OutputDebugStringA(buf);
	}

	return (TRUE == result);
}

LONGLONG SelectionDialog::GetAddress1() const
{
	return mAddress1;
}

LONGLONG SelectionDialog::GetAddress2() const
{
	return mAddress2;
}

// ------- private -------------------------

// static 
LRESULT CALLBACK SelectionDialog::WindowProcForEditField(HWND inHwndEdit,
	UINT inMsg, WPARAM wParam, LPARAM lParam)
{
	LONG_PTR param = ::GetWindowLongPtr(inHwndEdit, GWLP_USERDATA);
	auto pThis = reinterpret_cast<SelectionDialog *>(param);
	assert(nullptr != pThis);

	LRESULT result = ::CallWindowProc(pThis->mDefaultProcedureForEditField,
		inHwndEdit, inMsg, wParam, lParam);

	if (WM_CHAR == inMsg)
	{
		pThis->ValidateAddress(inHwndEdit);
	}

	return result;
}

// static 
INT_PTR CALLBACK SelectionDialog::DialogProc(HWND hwndDlg, UINT inMessage,
	WPARAM wParam, LPARAM lParam)
{
	INT_PTR result = FALSE;
	if (WM_INITDIALOG == inMessage)
	{
		HWND editField1 = ::GetDlgItem(hwndDlg, IDC_EDIT_RANGEEND1);
		HWND editField2 = ::GetDlgItem(hwndDlg, IDC_EDIT_RANGEEND2);
		if (editField1 == (HWND)wParam || editField2 == (HWND)wParam)
		{
			result = true;
		}
		if (auto pThis = reinterpret_cast<SelectionDialog *>(lParam))
		{
			pThis->LinkToDilogWindow(hwndDlg);
		}
	}
	else if (WM_COMMAND == inMessage)
	{
		switch (LOWORD(wParam))
		{
		case IDOK:
			::EndDialog(hwndDlg, TRUE);
			break;
		case IDCANCEL:
			::EndDialog(hwndDlg, FALSE);
			break;
		}
	}
	return result;
}

void SelectionDialog::ValidateAddress(HWND inEditField)
{
	LONGLONG& address = (inEditField == mEditField1 ? mAddress1 : mAddress2);

	// the number of hex digits is twice the number of bytes
	static const int kMaxDigits = 2 * sizeof(address);
	int n = ::GetWindowTextLengthA(inEditField);
	bool shouldEnable = (0 < n && n <= kMaxDigits);

	if (shouldEnable)
	{
		char text[kMaxDigits + 1];
		::GetWindowTextA(inEditField, text, sizeof(text));

		char* endPtr;
		address = std::strtoll(text, &endPtr, 16);
		shouldEnable = (address < mFileLength && &text[n] == endPtr);
	}
	if (inEditField == mEditField1)
	{
		mValid1 = shouldEnable;
	}
	else
	{
		mValid2 = shouldEnable;
	}

	::EnableWindow(mOkButton, (mValid1 && mValid2));
}

void SelectionDialog::LinkToDilogWindow(HWND inDialogWindow)
{
	mEditField1 = ::GetDlgItem(inDialogWindow, IDC_EDIT_RANGEEND1);
	mEditField2 = ::GetDlgItem(inDialogWindow, IDC_EDIT_RANGEEND2);
	mOkButton = ::GetDlgItem(inDialogWindow, IDOK);

	::SetWindowLongPtr(mEditField1, GWLP_USERDATA,
		reinterpret_cast<LONG_PTR>(this));
	::SetWindowLongPtr(mEditField2, GWLP_USERDATA,
		reinterpret_cast<LONG_PTR>(this));
	mDefaultProcedureForEditField = reinterpret_cast<WNDPROC>(
		::GetWindowLongPtr(mEditField1, GWL_WNDPROC));
	::SetWindowLongPtr(mEditField1, GWL_WNDPROC,
		reinterpret_cast<LONG_PTR>(&SelectionDialog::WindowProcForEditField));
	::SetWindowLongPtr(mEditField2, GWL_WNDPROC,
		reinterpret_cast<LONG_PTR>(&SelectionDialog::WindowProcForEditField));

	mValid1 = mValid1 = false;
	::EnableWindow(mOkButton, false);
}